package com.evolved.automata.android.bots.speechbot;

import android.app.Activity;
import android.content.*;
import android.os.Bundle;
import com.evolved.automata.*;
import android.app.*;
import java.util.*;

import android.text.Layout;
import android.util.Log;
import android.view.*;
import android.widget.*;
import android.content.*;
import android.content.res.*;
import android.speech.tts.*;

public class InferenceApp extends Activity implements LabelUpdater
{
    /** Called when the activity is first created. */
	
	public static String KEY_VALUE_SEPARATOR="";
	public static String PAIR_SEPARATOR="";
	public static String WORLD_SEPARATOR="";
	private static final int EXPANDED_DIALOG=3;
	private static final int SETTING_DIALOG=4;
	private static final int MOD_CONFIRMATION_DIALOG=5;
	private static final int CONSOLE_DIALOG=6;
	public static final int SPEECH_QUEUE_DIALOG=7;
	
	InferenceApp thisApp;
	Button transformStructButton;
	Button addStructButton;
	Button clearStructButton;
	Button inferenceButton;
	Button allConsistentWorldsButton;
	Button navigateForwardButton;
	Button navigateBackButton;
	EditText stackPositionField;
	
	DataInputPanel dPanel;
	public static InferenceController controller;
	boolean firstTime;
	FrameLayout dataPanel;
	String panelStatePref="PANEL_STATE";
	private static final String DEBUG_LABEL="Inference App";
	private boolean destroyedP;
	String logFileFullname;
	String[] stack=null;
	int stackPosition=0;
	int stackSize=0;
	MapComparer transformer;
	LinkedList<StackElement> sortOrder;
	
	boolean speechReadyP=false;
	boolean clearOnKeyStoreP;
	ExpandedOptionsDialog expandedOptionsDialog;
	Dialog settingsDialog;
	Dialog speechControllerDialog;
	Dialog consoleDialog;
	String[] speechPhrases;
	
	
	
	public SpeechController speechController;
	
	Hashtable<String,String> loadedMap=null;
	
	public static class StackElement
	{
		String keyName="";
		boolean ascendingP=true;
	}
	
	
	public InferenceApp()
	{
		super();
		thisApp=this;
		
	}
	
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
    	try
    	{
	        super.onCreate(savedInstanceState);
	        Resources res = getResources();
	        logFileFullname = res.getString(R.string.log_file_full_name);
			KEY_VALUE_SEPARATOR = res.getString(R.string.key_value_split_string);
			PAIR_SEPARATOR = res.getString(R.string.pair_split_string);
			WORLD_SEPARATOR = res.getString(R.string.stack_element_delimiter);
			clearOnKeyStoreP = res.getBoolean(R.bool.clear_on_save);
	        loadAndConfigureUIElements();
	        // Inference Related functions
	        initializeDatabase();
	        
	        // Create expanded key/value pair options dialog
	        expandedOptionsDialog = new ExpandedOptionsDialog(this,controller);
	        dPanel = new DataInputPanel(thisApp,controller);
			dataPanel.addView(dPanel);
			speechController = new SpeechController(this);
			speechController.start();
    	}
    	catch (Exception e)
    	{
    		writeError(e);
    		throw new RuntimeException(e);
    	}
    }
    
   @Override 
   public void onStart()
   {
	   super.onStart();
	   try
	   {
		   
		   restorePriorPanelState();
	   }
	   catch (Exception e)
	   {
		   writeError(e);
		   throw new RuntimeException(e);
	   }
	   
   }
   
   
   @Override
   public void onResume()
   {
	   super.onResume();
	   
   }
   
   @Override
   public void onPause()
   {
	   super.onPause();
	   
   }
   
   @Override
   public void onStop()
   {
	   saveDataPanelState();
	   speechController.stopPlayingCurrentText();
	   super.onStop();
	   
   }
    
    @Override
    public void onDestroy()
    {
    	speechController.shutdown();
    	super.onDestroy();
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
    	super.onCreateOptionsMenu(menu);
    	getMenuInflater().inflate(R.menu.main, menu);
    	return true;
    }
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item)
    {
    	if (item.getItemId()==R.id.exitMenuOption)
    	{
    		finish();
    		return true;
    	}
    	else if (item.getItemId()==R.id.wipeDBMenuOption)
    	{
    		wipeDBCommand();
    		return true;
    	}else if (item.getItemId()==R.id.cloneMenuOption)
    	{
    		String serializedPanel;
    		Hashtable<String,String> currentKeys = dPanel.getKeyValuePairs();
    		if (currentKeys.size()>0)
    		{
    			sortOrder=null;
    			serializedPanel=StandardTools.serializeToString(currentKeys);
    			stack=null;
        		stackPosition=0;
        		stackSize=2;
        		stack = new String[2];
        		stack[0]=stack[1]=serializedPanel;
        		UpdateStackPositionDisplay();
    		}
    		
    		
    		return true;
    	}
    	else if (item.getItemId()==R.id.clearStack)
    	{
    		stack=null;
    		stackPosition=0;
    		stackSize=0;
    		UpdateStackPositionDisplay();
    		return true;
    	}
    	else if (item.getItemId()==R.id.settingsMenuOption)
    	{
    		showDialog(SETTING_DIALOG);
    		return true;
    	} 
    	else if (item.getItemId()==R.id.showConsole)
    	{
    		showDialog(CONSOLE_DIALOG);
    		return true;
    	}
    	else 
    		return false;
    }
    
    @Override
    protected void onPrepareDialog(int id, Dialog theDialog, Bundle args) 
    {
        switch(id) 
        {
        	case MOD_CONFIRMATION_DIALOG:
        		TextView transactionDescription = (TextView)theDialog.findViewById(R.id.transactionTextView);
        		transactionDescription.setText(transformer.getTransactionDescription());
        		break;
        	case SPEECH_QUEUE_DIALOG:
        		EditText phraseView = (EditText)speechControllerDialog.findViewById(R.id.spokenPhraseView);
        		String phraseText = speechController.getCurrentText();
        		
        		if (phraseText!=null)
        		{
        			phraseView.setText(phraseText);
        			updateCurrentSpeechPosition();
        		}
        		else
        			phraseView.setText("");
        		break;
        	case CONSOLE_DIALOG:
        		controller.loadQuestionMap();
        		break;
        }
        
    }
    
    
    @Override
    protected Dialog onCreateDialog(int id) {
        Dialog dialog=null;
        switch(id) 
        {
	        case EXPANDED_DIALOG:
	        	if (expandedOptionsDialog==null)
	        	{
	        		expandedOptionsDialog = new ExpandedOptionsDialog(this,controller);
	        	}
	        	dialog=expandedOptionsDialog;
	        	break;
	        case MOD_CONFIRMATION_DIALOG:
	        	dialog = createTransformConfirmationDialog();
	        	break;
	        case SETTING_DIALOG:
	        	dialog=createSettingsDialog();
	        	break;
	        case CONSOLE_DIALOG:
	        	dialog=createConsoleDialog();
	        	break;
	        case SPEECH_QUEUE_DIALOG:
	        	dialog=createSpeechQueueDialog();
	        	break;
	        default:
	            dialog = null;
        }
        return dialog;
    }
    
    public Dialog createSpeechQueueDialog()
    {
    	speechControllerDialog = new Dialog(thisApp);
    	speechControllerDialog.setContentView(R.layout.speech_dialog);
    	Button backButton = (Button)speechControllerDialog.findViewById(R.id.speechBackButton);
    	Button forwardButton = (Button)speechControllerDialog.findViewById(R.id.speechNextButton);
    	Button stopButton = (Button)speechControllerDialog.findViewById(R.id.speechStopButton);
    	Button playButton = (Button)speechControllerDialog.findViewById(R.id.speechContinueButton);
    	Button cancelButton = (Button)speechControllerDialog.findViewById(R.id.speechCancelButton);
    	
    	backButton.setOnClickListener(new View.OnClickListener() 
    	{
			
			@Override
			public void onClick(View v) 
			{
				speechController.playPrevText();
				int currentPosition=speechController.getCurrentMessageIndex(); // block until new index returned
				int totalMessages=speechController.getTotalMessages();
				String newText = speechController.getCurrentText();
				if (newText!=null)
				{
					EditText phraseView = (EditText)speechControllerDialog.findViewById(R.id.spokenPhraseView);
					phraseView.setText(newText);
					updateCurrentSpeechPosition();
				}
			}
		});
    	
    	forwardButton.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				speechController.playNextText();
				int currentPosition=speechController.getCurrentMessageIndex(); // block until new index returned
				int totalMessages=speechController.getTotalMessages();
				String newText=speechController.getCurrentText();
				if (newText!=null)
				{
					EditText phraseView = (EditText)speechControllerDialog.findViewById(R.id.spokenPhraseView);
					phraseView.setText(newText);
					updateCurrentSpeechPosition();
				}
			}
		});
    	
    	stopButton.setOnClickListener(new View.OnClickListener() 
    	{
			
			@Override
			public void onClick(View v) 
			{
				speechController.stopPlayingCurrentText();
				
			}
		});
    	
    	playButton.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) 
			{
				
				speechController.playCurrentText();
				
			}
		});
    	
    	cancelButton.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				speechController.stopPlayingCurrentText();
				
				speechControllerDialog.dismiss();
			}
		});
    	
    	return speechControllerDialog;
    }

    public Dialog createConsoleDialog()
    {
    	consoleDialog = new Dialog(thisApp);
    	consoleDialog.setContentView(R.layout.console_panel);
    	Button accept = (Button)consoleDialog.findViewById(R.id.consoleOkayButton);
    	Button cancel = (Button)consoleDialog.findViewById(R.id.consoleCancelButton);
    	
    	RadioButton speakFirst = (RadioButton)consoleDialog.findViewById(R.id.speakAllAnswersRadioButton);
    	
    	
    	
    	
    	cancel.setOnClickListener(new View.OnClickListener() 
    	{
			
			@Override
			public void onClick(View v) 
			{
				consoleDialog.dismiss();
				
			}
		});
    	
    	accept.setOnClickListener(new View.OnClickListener() 
    	{
			
			@Override
			public void onClick(View v) 
			{
				CheckBox speechRequested = (CheckBox)consoleDialog.findViewById(R.id.speakSearchAnswerCheckbox);
				boolean speakResultsP = speechRequested.isChecked();
				MultiAutoCompleteTextView multiComplete = (MultiAutoCompleteTextView)consoleDialog.findViewById(R.id.consoleInputBoxTextView);
				String searchText = multiComplete.getText().toString();
				// Get the most likely values
				String[] consistentQuestions=controller.processQuestionText(searchText);
				if (consistentQuestions==null)
				{
					showErrorToast("Unrecognizable search text");
					return;
				}
				if (consistentQuestions.length==0)
				{
					showErrorToast("Ambiguous search text.  Please remove extraneous words");
				}
				Hashtable<String,String> responseMap;
				int resultCount=0;
				
				KeyValuePair[] speechKeys;
				String[] speechPhrases;
				if (consistentQuestions.length==1)
				{
					// Either multiple possible search questions or one search answer
					responseMap = StandardTools.parseToKeyValueMap(consistentQuestions[0]);
					
					dPanel.clearAll();
					dPanel.addKeyValuePairsToList(responseMap);
					if (speakResultsP)
					{
						speechKeys=getSpeechSubMap(responseMap);
						speechPhrases=new String[speechKeys.length];
						if (speechKeys.length>0)
						{
							for (int i=0;i<speechKeys.length;i++)
							{
								speechPhrases[i]=speechKeys[i].GetValue();
							}
							speechController.updateMessages(speechPhrases);
							showDialog(SPEECH_QUEUE_DIALOG);
							
						}
					}
					consoleDialog.dismiss();
				}
				else
				{
					stack=consistentQuestions;
					stackPosition=0;
					stackSize=consistentQuestions.length;
					StandardTools.sortStack(stack, new String[]{"Step"}, true);
					UpdateDataPanelFromStack();
					RadioButton speakAll = (RadioButton)consoleDialog.findViewById(R.id.speakAllAnswersRadioButton);
					if (speakResultsP)
					{
						
						LinkedList<String> totalSpeechPhrases = new LinkedList<String>();
						boolean first=false;
						for (String world:consistentQuestions)
						{
							responseMap = StandardTools.parseToKeyValueMap(world);
							speechKeys=getSpeechSubMap(responseMap);
							if (speechKeys.length>0)
							{
								for (int i=0;i<speechKeys.length;i++)
								{
									totalSpeechPhrases.add(speechKeys[i].GetValue());
								}
								if (!speakAll.isChecked())
									break;
							}
							
						}
						
						if (totalSpeechPhrases.size()>0)
						{
							
							speechController.updateMessages(totalSpeechPhrases.toArray(new String[0]));
							showDialog(SPEECH_QUEUE_DIALOG);
						}
					}
					
					consoleDialog.dismiss();
				}
			}
		});
    	
    	return consoleDialog;
    }
    
    public void updateCurrentSpeechPosition()
    {
    	int currentPosition = speechController.getCurrentMessageIndex();
    	int total = speechController.getTotalMessages();
    	if ((speechControllerDialog!=null)&&(speechControllerDialog.isShowing()))
    	{
    		EditText phraseView = (EditText)speechControllerDialog.findViewById(R.id.speechPositionIndicatorView);
    		phraseView.setText(String.format("%1$s of %2$s", currentPosition+1,total));
    	}
    }
    
    
    
    public boolean addSortElement(String sortKey, boolean ascendingP)
    {
    	
    	StackElement ele = new  StackElement();
    	ele.ascendingP=ascendingP;
    	ele.keyName=sortKey;
    	
    	if (sortOrder==null)
    	{
    		sortOrder = new LinkedList<InferenceApp.StackElement>();
    	}
		for (StackElement ele2:sortOrder)
		{
			if (ele2.keyName.equals(sortKey))
			{
				if (ele2.ascendingP==ascendingP)
					return false;
				else
				{
					ele2.ascendingP=ascendingP;
					return true;
				}
			}
		}
		
		sortOrder.add(ele);
		return true;
    }
    
    public void removeSortElement(String sortKey, boolean ascendingP)
    {
    	
    	StackElement ele2=null;
    	
    	if (sortOrder!=null)
    	{
    		for (StackElement ele:sortOrder)
    		{
    			if (ele.keyName.equals(sortKey))
    			{
    				ele2=ele;
    				break;
    			}
    		}
    		if (ele2!=null)
    			sortOrder.remove(ele2);
    	}
    }
    
    public void UpdateSort()
    {
    	UpdateDataPanelFromStack(true);
    }
    
    
    private Dialog createSettingsDialog()
    {
    	settingsDialog = new Dialog(thisApp);
    	settingsDialog.setContentView(R.layout.settings_dialog_layout);
    	
    	Button importExportButton = (Button)settingsDialog.findViewById(R.id.importExportButton);
    	importExportButton.setOnClickListener(getImportExportClickListener());
    	   	
    	return settingsDialog;
    }
    
    
    private View.OnClickListener getImportExportClickListener()
    {
    	return new View.OnClickListener() 
    	{
			
			@Override
			public void onClick(View v) 
			{
				if (v.getId()==R.id.importExportButton)
				{
					RadioButton importButton = (RadioButton)settingsDialog.findViewById(R.id.importRadioButton);
					RadioButton exportButton = (RadioButton)settingsDialog.findViewById(R.id.exportStackRadioButton);
					EditText fileEditText = (EditText)settingsDialog.findViewById(R.id.importExportFileNameEditText);
					String fileFullName = fileEditText.getText().toString();
					if (importButton.isChecked())
					{
						String[] newStack=null;
						try
						{
							newStack = controller.getFromCSV(fileFullName);
							if ((newStack==null)||(newStack.length==0))
								showErrorToast("No data to load");
							else
							{
								stack=newStack;
								stackPosition=0;
								stackSize=newStack.length;
								
								CheckBox autoImport = (CheckBox)settingsDialog.findViewById(R.id.importToStackOnlyCheckbox);
								if (!autoImport.isChecked())
								{
									Hashtable<String,String> frame;
									for (String map:newStack)
									{
										frame = StandardTools.parseToKeyValueMap(map);
										controller.saveKeyValueMap(frame);
									}
								}
							}
							settingsDialog.dismiss();
						}
						catch (Exception e)
						{
							showErrorToast("Failure loading from file");
						}
					}
					else
					{
						if (exportButton.isChecked()&&stack!=null&&stack.length>0)
						{
							try
							{
								
								controller.writeWorldsToCSVFile(fileFullName, stack);
								settingsDialog.dismiss();
							}
							catch (Exception e)
							{
								showErrorToast("Failure saving to file");
							}
						}
						else
						{
							showErrorToast("Nothing to do");
							settingsDialog.dismiss();
						}
					}
					
				}
				
			}
		};
    }
    
    
    
    private Dialog createTransformConfirmationDialog()
    {
    	Dialog dialog = new Dialog(thisApp);
    	dialog.setContentView(R.layout.mod_confirmation_dialog);
    	
    	Button okayButton = (Button)dialog.findViewById(R.id.transactionAcceptButton);
    	Button cancelButton = (Button)dialog.findViewById(R.id.transactionCancelButton);
    	
    	cancelButton.setOnClickListener(new View.OnClickListener() 
    	{
			@Override
			public void onClick(View v) 
			{
				
				dismissDialog(MOD_CONFIRMATION_DIALOG);
			}
		});
    	
    	okayButton.setOnClickListener(new View.OnClickListener() 
    	{
			
			@Override
			public void onClick(View v) 
			{
				Hashtable<String,String> baseKeys = transformer.getBaseKeys();
				Hashtable<String,String> addedKeys = transformer.getAddedKeys();
				Hashtable<String,String> deletedKeys = transformer.getDeletedKeys();
				Hashtable<String,String> changedKeys= transformer.getChangedKeys();
				try
				{
					boolean changesMade=false;
					if (transformer.getCompMap().size()==0)
					{
						changesMade=true;
						controller.deleteConsistentWorlds(baseKeys);
					}
					else
					{
						List<Integer> worldsToChange = controller.getAllConsistentWorldIndices(baseKeys);
						
						for (String addedKey:addedKeys.keySet().toArray(new String[0]))
						{
							changesMade=true;
							controller.addKeyValueToConsistentWorlds(worldsToChange, addedKey, addedKeys.get(addedKey));
						}
						for (String deletedKey:deletedKeys.keySet().toArray(new String[0]))
						{
							changesMade=true;
							controller.removeKeyFromConsistentWorldIndices(worldsToChange,deletedKey);
						}
						
						for (String changedKey:changedKeys.keySet().toArray(new String[0]))
						{
							changesMade=true;
							controller.changeValueInConsistentWorldIndices(worldsToChange, changedKey, changedKeys.get(changedKey));
						}
						
						controller.synchronizeModelWithDB(worldsToChange);
						
					}
					UpdateDataPanelFromStack();
					if (changesMade)
						showToast("level 1 modified");
					
				}
				catch (Exception e)
				{
					showToast("Errors modifying worlds");
					writeError(e);
				}
				finally
				{
					dismissDialog(MOD_CONFIRMATION_DIALOG);
				}
			}
		});
    	
    	
    	return dialog;
    }
    

    
    private void processTransformAction()
    {
    	if (stackSize>1)
    	{
    		Hashtable<String,String> changedMap = dPanel.getKeyValuePairs();
    		String serializedBaseMap = stack[0];
    		if (serializedBaseMap.length()>0)
    		{
    			Hashtable<String,String> baseMap = StandardTools.parseToKeyValueMap(serializedBaseMap);
    			transformer = new MapComparer(baseMap, changedMap);
    			showDialog(MOD_CONFIRMATION_DIALOG);
    		}
    	}
    }
    
    
    public void showExpandedDialog(EditText baseKey, EditText baseValue, boolean keyIsReadOnlyP)
    {
    	expandedOptionsDialog.setViewsToUpdate(baseKey, baseValue,keyIsReadOnlyP);
    	showDialog(EXPANDED_DIALOG);
    }
    

    
    
    public boolean updateLabel(int viewId, String newText)
    {
    	View viewObj = findViewById(viewId);
    	TextView tview;
    	Button button;
    	boolean rValue=false;
    	if (viewObj!=null)
    	{
    		if (viewObj instanceof TextView)
    		{
    			tview = (TextView)viewObj;
    			tview.setText(newText);
    			rValue=true;
    		}
    		else if (viewObj instanceof Button)
    		{
    			button = (Button)viewObj;
    			button.setText(newText);
    			rValue=true;
    		}
    	}
    	return rValue;
    }
    
    public void showToast(String message)
    {
    	Toast t = Toast.makeText(thisApp, message, Toast.LENGTH_SHORT);
    	t.show();
    }
    
    public void showLongToast(String message)
    {
    	Toast t = Toast.makeText(thisApp, message, Toast.LENGTH_LONG);
    	t.show();
    }
    
    private void wipeDBCommand()
    {
    	AlertDialog.Builder alertBuilder = new AlertDialog.Builder(thisApp);
    	alertBuilder.setMessage(getResources().getString(R.string.wipe_warning_message));
    	alertBuilder.setPositiveButton(getResources().getString(R.string.wipe_confirm_text), getDialogListenerForWipeConfirm());
    	alertBuilder.setNegativeButton(getResources().getString(R.string.wipe_cancel_text), getDialogListenerForSimpleCancel());
    	alertBuilder.setTitle("wipe all data?");
    	
    	try
    	{
    		alertBuilder.show();
    	}
    	catch (Exception e)
    	{
    		Log.d("Inference",e.toString());
    	}
    }
   
    
    private DialogInterface.OnClickListener getDialogListenerForWipeConfirm()
    {
    	return new DialogInterface.OnClickListener() {
			
			@Override
			public void onClick(DialogInterface dialog, int which) 
			{
				controller.deleteAllStructures();
				dialog.dismiss();
			}
		};
    }
    
    
    private DialogInterface.OnClickListener getDialogListenerForSimpleCancel()
    {
    	return new DialogInterface.OnClickListener() {
			
			@Override
			public void onClick(DialogInterface dialog, int which) {
				// TODO Auto-generated method stub
				dialog.dismiss();
				
			}
		};
    }
    
    
    private void restorePriorPanelState()
    {
    	SharedPreferences preferences = getPreferences(MODE_PRIVATE);
    	String serialized = preferences.getString(panelStatePref, "");
    	if ((serialized.length()>0)&&(dPanel!=null))
    	{
    		Hashtable<String,String> struct = StandardTools.parseToKeyValueMap(serialized);
    		dPanel.addKeyValuePairsToList(struct);
    	}
    	
    	Resources res = getResources();
    	String serializedWorlds = getStringPreferenceSetting(res.getString(R.string.stack_definition), "");
    	
    	if (serializedWorlds.length()>0)
    	{
    		stack = StandardTools.parseSerializedStringToWorlds(serializedWorlds);
    		stackPosition= getIntPreferenceSetting(res.getString(R.string.stack_position), 0);
    		stackSize=getIntPreferenceSetting(res.getString(R.string.stack_size), 0);
    		UpdateStackPositionDisplay();
    		UpdateDataPanelFromStack();
    	}
    	else
    	{
    		stack=null;
    		stackPosition=0;
        	stackSize=0;
    	}
		
    }
    
    private void saveDataPanelState()
    {
    	if (dPanel!=null)
    	{
	    	Hashtable<String,String> struct = dPanel.getKeyValuePairs();
	    	SharedPreferences preferences = getPreferences(MODE_PRIVATE);
	    	SharedPreferences.Editor pEditor=preferences.edit();
	    	String serialized="";
	    	if (struct.size()>0)
	    	{
	        	
	    		serialized = StandardTools.serializeToString(struct);
	    		pEditor.putString(panelStatePref, serialized);
	    	}
	    	else
	    		pEditor.remove(panelStatePref);
	    	pEditor.commit();
	    	
	    	String serializedWorlds="";
	    	if ((stack!=null)&&(stack.length>0))
	    	{
	    		serializedWorlds = StandardTools.serializeToString(stack);
	    	}
	    	
    		Resources res = getResources();
    		setStringPreferenceSetting(res.getString(R.string.stack_definition), serializedWorlds);
    		setIntPreferenceSetting(res.getString(R.string.stack_position), stackPosition);
    		setIntPreferenceSetting(res.getString(R.string.stack_size), stackSize);
	    		
	    	
    	}
    }
    
    
    
    private void loadAndConfigureUIElements()
    {
    	setContentView(R.layout.structure_panel);
        dataPanel = (FrameLayout)findViewById(R.id.central_structure);
        transformStructButton = (Button)findViewById(R.id.transformStructButton);
        addStructButton= (Button)findViewById(R.id.saveButton);
        clearStructButton= (Button)findViewById(R.id.clearButton);
        inferenceButton= (Button)findViewById(R.id.quickInferenceButton);
        allConsistentWorldsButton = (Button)findViewById(R.id.selectAllButton);
        navigateForwardButton = (Button)findViewById(R.id.navForwardButton);
        navigateBackButton = (Button)findViewById(R.id.navBackButton);
        stackPositionField = (EditText)findViewById(R.id.positionIndicator);
        
        
        
        allConsistentWorldsButton.setOnClickListener(getAllConsistentWorldsClickListener());
        
        navigateForwardButton.setOnClickListener(getNavigationForwardClickListener());
        
        navigateBackButton.setOnClickListener(getNavigationBackClickListener());
        
        stackPositionField.setOnFocusChangeListener(getPositionNavigationFocusListener());
        
        transformStructButton.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) 
			{
				processTransformAction();
			}
		});
        
        clearStructButton.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) 
			{
				if (dPanel!=null)
				{
					dPanel.clearAll();
				}
				
			}
		});
        
        addStructButton.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v)
			{
				Hashtable<String,String> mapData;
				mapData = dPanel.getKeyValuePairs();
				try
				{
					boolean success=false;
					if (mapData.size()>0)
					{
						success= controller.saveKeyValueMap(mapData);
					}
					
					if (success)
					{
						showToast("current panel saved");
						if (clearOnKeyStoreP)
							dPanel.clearAll();
					}
					
					setDefaultMainTile();
				}
				catch (Exception e)
				{
					writeLog("Error during save!");
					writeError(e);
				}
			}
		});
        
        inferenceButton.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) 
			{
				try
				{
					String staticInferenceData = getStringPreferenceSetting("fixed_structure_values", "");
					Hashtable<String,String> mapData, inferenceData;
					mapData = dPanel.getKeyValuePairs();
					inferenceData=mapData;
					if (staticInferenceData.length()>0)
					{
						setStringPreferenceSetting("fixed_structure_values", "");
						Hashtable<String,String> fixedKeyValues;
						fixedKeyValues = StandardTools.parseToKeyValueMap(staticInferenceData);
						StandardTools.mergeKeyValues(mapData, fixedKeyValues);
						inferenceData=fixedKeyValues;
					}
					
					mapData = controller.executeInferenceOperation(inferenceData);
					setDefaultMainTile();
					// TODO: Make this only update the values that are different instead of clearing
					dPanel.clearAll();
					dPanel.addKeyValuePairsToList(mapData);
				}
				catch (Exception e)
				{
					writeLog("Error during inference");
					writeError(e);
				}
			}
		});
    }
    
    
    private View.OnClickListener getAllConsistentWorldsClickListener()
    {
    	
    	return new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				Hashtable<String,String> dataFields;
		    	if (dPanel!=null)
		    	{
		    		dataFields = dPanel.getKeyValuePairs();
		    		if ((dataFields!=null)&&(dataFields.size()>0))
		    		{
		    			stack = controller.getAllConsistentWorlds(dataFields);
		    			if ((stack==null)||(stack.length==0))
		    			{
		    				showToast("No consistent worlds");
		    			}
		    			else
		    			{
		    				sortOrder=null;
			    			stackPosition=0;
			    			UpdateStackPositionDisplay();
			    			UpdateDataPanelFromStack();
			    			dataFields=StandardTools.parseToKeyValueMap(stack[0]);
			    			String robotTriggerKey="robot_function";
			    			String robotTriggerValue="start";
			    			
			    			if (dataFields.containsKey(robotTriggerKey))
			    			{
			    				if (dataFields.get(robotTriggerKey).equals(robotTriggerValue))
			    				{
			    					Intent robotController = new Intent(getApplicationContext(),SpeechBot.class);
			    					startActivity(robotController);
			    				}
			    			}
		    			}
		    		}
		    	}
			}
		};
    	
    	
    }
    
    private View.OnClickListener getNavigationForwardClickListener()
    {
    	return new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				if (stack!=null)
		    	{
		    		stackPosition = (stackPosition+1)%stackSize;
		    		UpdateStackPositionDisplay();
		    		UpdateDataPanelFromStack();
		    	}
			}
		};
    	
    	
    }
    
    private View.OnClickListener getNavigationBackClickListener()
    {
    	return new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				if (stack!=null)
		    	{
					stackPosition=(stackPosition==0)?Math.max(0, stackSize-1):stackPosition-1;
		    		
		    		UpdateStackPositionDisplay();
		    		UpdateDataPanelFromStack();
		    	}
			}
		};
    }
    
    private View.OnFocusChangeListener getPositionNavigationFocusListener()
    {
    	return new View.OnFocusChangeListener() {
			
			@Override
			public void onFocusChange(View v, boolean hasFocus) {
				if (!hasFocus)
				{
					boolean updatePosP=false;
					String newPosString = stackPositionField.getText().toString();
					String numberPattern = getResources().getString(R.string.number_pattern);
					if (java.util.regex.Pattern.matches(numberPattern,newPosString))
					{
						int newPos = Integer.parseInt(newPosString)-1;
						if ((newPos!=(stackPosition))&&(newPos>=0)&&(newPos<stackSize))
						{
							stackPosition=newPos;
							updatePosP=true;
						}
					}
					
					if (updatePosP)
					{
						
			    		UpdateDataPanelFromStack();
					}
					UpdateStackPositionDisplay();
				}
				else
				{
					stackPositionField.setText("");
				}
				
				
			}
		};
    	
    	
    }
    
    private void UpdateDataPanelFromStack()
    {
    	if ((stack!=null)&&(stack.length>0))
    	{
	    	String world = stack[stackPosition];
	    	Hashtable<String,String> keyValueMap = StandardTools.parseToKeyValueMap(world);
	    	if (dPanel!=null)
	    	{
	    		dPanel.clearAll();
	    		dPanel.addKeyValuePairsToList(keyValueMap);
	    	}
    	}
    }
    
    private void UpdateDataPanelFromStack(boolean sort)
    {
    	try
    	{
	    	if ((stack!=null)&&(stack.length>0))
	    	{
	    		if (sort)
	    		{
	    			if ((sortOrder!=null)&&(sortOrder.size()>0))
	    			{
	    				String[] column = new String[sortOrder.size()];
	    				boolean asc=false;
	    				StackElement ele;
	    				for (int i=0;i<sortOrder.size();i++)
	    				{
	    					ele=sortOrder.get(i);
	    					column[i]=ele.keyName;
	    					asc=ele.ascendingP;
	    				}
	    				
	    				String world = stack[stackPosition];
	    				StandardTools.sortStack(stack, column, asc);
	    				for (int i=0;i<stack.length;i++)
	    				{
	    					if (world.equals(stack[i]))
	    					{
	    						stackPosition=i;
	    						return;
	    					}
	    					
	    				}
	    				stackPosition=0;
	    			}
	    		}
		    	
		    	
	    	}
    	}
    	catch (Exception e)
    	{
    		writeError(e);
    	
    	}
    }
    
    private void UpdateStackPositionDisplay()
    {
    	if (stack==null)
    	{
    		stackPositionField.setText(getResources().getString(R.string.default_position_indicator));
    	}
    	else
    	{
    		stackSize = stack.length;
    		String stackDisplay = String.format("%1$s of %2$s", stackPosition+1, Math.max(stackSize, 1));
    		stackPositionField.setText(stackDisplay);
    		
    	}
    	
    }
    
    private DialogInterface.OnClickListener getErrorConfirmationListener()
    {
    	return new DialogInterface.OnClickListener() {
			
			@Override
			public void onClick(DialogInterface dialog, int which) {
				// TODO Auto-generated method stub
				dialog.dismiss();
				
			}
		};
    }
    
    public String getStringPreferenceSetting(String settingName, String defaultIfNotDefined)
    {
    	SharedPreferences preferences = getPreferences(MODE_PRIVATE);
    	return preferences.getString(settingName, defaultIfNotDefined);
    }
    
    public boolean getBooleanPreferenceSetting(String settingName, boolean defaultIfNotDefined)
    {
    	SharedPreferences preferences = getPreferences(MODE_PRIVATE);
    	return preferences.getBoolean(settingName, defaultIfNotDefined);
    }
    
    public int getIntPreferenceSetting(String settingName, int defaultIfNotDefined)
    {
    	SharedPreferences preferences = getPreferences(MODE_PRIVATE);
    	return preferences.getInt(settingName, defaultIfNotDefined);
    }
    
    public void setStringPreferenceSetting(String settingName, String newValue)
    {
    	SharedPreferences preferences = getPreferences(MODE_PRIVATE);
    	SharedPreferences.Editor edit = preferences.edit();
    	edit.putString(settingName, newValue);
    	edit.commit();
    }
    
    public void setBooleanPreferenceSetting(String settingName, boolean newValue)
    {
    	SharedPreferences preferences = getPreferences(MODE_PRIVATE);
    	SharedPreferences.Editor edit = preferences.edit();
    	edit.putBoolean(settingName, newValue);
    	edit.commit();
    }
    
    public void setIntPreferenceSetting(String settingName, int newValue)
    {
    	SharedPreferences preferences = getPreferences(MODE_PRIVATE);
    	SharedPreferences.Editor edit = preferences.edit();
    	edit.putInt(settingName, newValue);
    	edit.commit();
    }
    
    
    private void initializeDatabase()
    {
    	int msize = getResources().getInteger(R.integer.model_size);
    	String dbNme = getResources().getString(R.string.db_name);
    	SharedPreferences preferences = getPreferences(MODE_PRIVATE);
    	SharedPreferences.Editor pEditor=preferences.edit();
    	firstTime = preferences.getBoolean("INITIAL", true);
    	controller = new InferenceController(thisApp, msize, dbNme, firstTime, thisApp);
    	
    	pEditor.putBoolean("INITIAL", false);
    	pEditor.commit();
    }
    
    private KeyValuePair[] getSpeechSubMap(Hashtable<String,String> dataMap)
    {
    	// TODO: Make this more efficient
    	String[] speechKeys = getResources().getStringArray(R.array.speech_keys);
    	Hashtable<String,String> speechMap = new Hashtable<String, String>();
    	for (String sKey: speechKeys)
    	{
    		if (dataMap.containsKey(sKey))
    			speechMap.put(sKey,dataMap.get(sKey));
    	}
    	return StandardTools.parseToKeyValuePairsFromString(speechMap);
    }
    
    private void setDefaultMainTile()
    {
    	String titleKey = getResources().getString(R.string.status_bar_text);
    	String standardHeader = getResources().getString(R.string.structure_title);
    	setStringPreferenceSetting(titleKey, standardHeader);
    	updateLabel(R.id.mainStatusBar, standardHeader);
    }
    
    private void setEditFactTemplateMainTile()
    {
    	String titleKey = getResources().getString(R.string.status_bar_text);
    	String standardHeader = getResources().getString(R.string.edit_fact_template_title);
    	setStringPreferenceSetting(titleKey, standardHeader);
    	updateLabel(R.id.mainStatusBar, standardHeader);
    }
    
    private void setEditQueryTemplateMainTile()
    {
    	String titleKey = getResources().getString(R.string.status_bar_text);
    	String standardHeader = getResources().getString(R.string.edit_query_template_title);
    	setStringPreferenceSetting(titleKey, standardHeader);
    	updateLabel(R.id.mainStatusBar, standardHeader);
    }
    
    private void setEditQueryParametersTemplateMainTile()
    {
    	String titleKey = getResources().getString(R.string.status_bar_text);
    	String standardHeader = getResources().getString(R.string.parameter_query_template_title);
    	setStringPreferenceSetting(titleKey, standardHeader);
    	updateLabel(R.id.mainStatusBar, standardHeader);
    }
    
    private void setEditFactParametersTemplateMainTile()
    {
    	String titleKey = getResources().getString(R.string.status_bar_text);
    	String standardHeader = getResources().getString(R.string.parameter_fact_template_title);
    	setStringPreferenceSetting(titleKey, standardHeader);
    	updateLabel(R.id.mainStatusBar, standardHeader);
    }
    
    private void writeLog(String message)
    {
    	String dateString = StandardTools.getCurrentDateString();
    	String log = String.format("%1$s: %2$s", dateString, message);
    	java.io.File f = new java.io.File(logFileFullname+dateString+".txt");
    	if (f.exists())
    	{
    		StandardTools.writeToFile(logFileFullname+dateString+".txt", log);
    	}
    	else
    	{
    		try
    		{
	    		if (f.createNewFile())
	    		{
	    			StandardTools.writeToFile(logFileFullname+dateString+".txt", log);
	    		}
    		}
    		catch (Exception e)
    		{
    			
    		}
    	}
    }

    private void writeError(Exception e)
    {
    	java.io.StringWriter traceText = new java.io.StringWriter();
		java.io.PrintWriter pWriter = new java.io.PrintWriter(traceText,true);
		e.printStackTrace(pWriter);
		pWriter.close();
		writeLog(traceText.toString());
    }
	
    
	
	public void showErrorToast(String error)
	{
		showToast(error);
	}
    
    
    
}